package jp.sourceforge.akjrcp.ffmpeg;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

import jp.sourceforge.akjrcp.util.ProcessTimeoutDestroyer;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;



public  abstract class FFMpegFLVFileEffector {
	private String ffmpegPath;
	private File inputFile;
	private File outputFile;
	private int bitrate=2000;//almost max.
	
	private  FFMpegJpegPipeReader fFMpegJpegPipeReader;
	private  FFMpegJpegPipeWriter fFMpegJpegPipeWriter;

	private  boolean readyRead;
	private  boolean readyWrite;
	private  File tmpMP3;
	
	private int inputImageType;
	private int outputImageType;
	public static final int IMAGE_JPEG=0;
	public static final int IMAGE_BMP=1;
	
	private boolean doneRead;
	private boolean doneWrite;
	public static Log log = LogFactory.getLog(FFMpegFLVFileEffector.class);
	
	public FFMpegFLVFileEffector(String ffmpegPath,File input,File output){
		this.ffmpegPath=ffmpegPath;
		inputFile=input;
		outputFile=output;
	}
	//should overwrite
	public abstract void effect(File inputFile,File outputFile);
	
	public void start(){
		FLVInfomation info=new FFMpegFLVParser(ffmpegPath).parseFile(new File(inputFile.getAbsolutePath())); 
		log.info(info.toString());
		if(info.isHaveAudio()){
		//do only audio available file
		try {
			tmpMP3 = File.createTempFile("temp",".mp3");
			tmpMP3.deleteOnExit();
			String[] mp3copy=FFMpegCommandUtil.copyAudioCommand(ffmpegPath, inputFile.getAbsolutePath(), tmpMP3.getAbsolutePath());
			SimpleCommandExecuter executer=new SimpleCommandExecuter(mp3copy);
			executer.execute();
		} catch (IOException e) {
			
			e.printStackTrace();
		}
		}
	//	log.info(tmpMP3.getAbsolutePath());
		
		
		
		fFMpegJpegPipeReader=new FFMpegJpegPipeReader(ffmpegPath,inputFile.getAbsolutePath(),new String[]{});
	//	log.info(ArrayUtils.toString(reader.getCommand()));
		fFMpegJpegPipeReader.setProcessListener(new IProcesListener());
		Thread t=new Thread(fFMpegJpegPipeReader);
		t.start();
		
		
		//input video rate is important,"-r",""+info.getVideoRate()
		if(info.isHaveAudio()){
			fFMpegJpegPipeWriter=new FFMpegJpegPipeWriter(ffmpegPath,true,new String[]{"-r",""+info.getVideoRate()},new String[]{"-i",tmpMP3.getAbsolutePath(),"-b",bitrate+"k","-vcodec","flv","-acodec","copy","-r",""+info.getVideoRate()},outputFile.getAbsolutePath());//,"-sameq"
			
		}else{
			//no audio
			fFMpegJpegPipeWriter=new FFMpegJpegPipeWriter(ffmpegPath,true,new String[]{"-r",""+info.getVideoRate()},new String[]{"-b",bitrate+"k","-vcodec","flv","-r",""+info.getVideoRate(),"-an"},outputFile.getAbsolutePath());//,"-sameq"
			
		}
		
		log.info(ArrayUtils.toString(fFMpegJpegPipeWriter.getCommand()));
		fFMpegJpegPipeWriter.setProcessListener(new IProcesListener2());
		Thread t2=new Thread(fFMpegJpegPipeWriter);
		t2.start();
		
		Thread ready=new Thread(new ReadyGo());
		ready.start();
	}
	
	public void cancel(){
		fFMpegJpegPipeReader.cancel();
		fFMpegJpegPipeWriter.cancel();
	}
	

	 public class IProcesListener implements ProcessListener{

		public void finished() {
	//		log.infofinished read");
			doneRead=true;
			/*
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
			writer.closeInput();
			writer.closeError();
			*/
			
			/*
			 * we don't need anymore
			try {
				Thread.sleep(15000);//final action
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
			
			fFMpegJpegPipeWriter.cancel();//for test.BUG.sometime drop final 1 frame.
			*/
			
		}

		public void started() {
	//		log.infoready read");
			readyRead=true;
		}
		
	}
	
	 public class IProcesListener2 implements ProcessListener{

		public void finished() {
	//		log.infofinished write");
			doneWrite=true;
		}

		public void started() {
	//		log.infoready write");
			readyWrite=true;
		}
		
	}
	
	 public class ReadyGo implements Runnable{
		public void run(){
			while(readyRead==false || readyWrite==false){
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
			}
		//	log.infogo");
			try {
				fileConvert(fFMpegJpegPipeReader.getJpegPipeReader(),fFMpegJpegPipeWriter.getJpegPipeWriter());
			} catch (IOException e) {
				
				e.printStackTrace();
			}
		}
	}
	
	public static long doneFrames=0;
	
	
	
	private File tmpInputImage;
	private File tmpOutputImage;
	private void fileConvert(JpegPipeReader reader,JpegPipeWriter writer) throws IOException{
		byte[] imgbyte=null;
		if(inputImageType==IMAGE_JPEG){
			tmpInputImage=File.createTempFile("effect", ".jpg");
		}else if(inputImageType==IMAGE_BMP){
			tmpInputImage=File.createTempFile("effect", ".bmp");
		}
		
		tmpInputImage.deleteOnExit();
		if(outputImageType==IMAGE_JPEG){
			tmpOutputImage=File.createTempFile("effect", ".jpg");
		}else if(outputImageType==IMAGE_BMP){
			tmpOutputImage=File.createTempFile("effect", ".bmp");
		}
		tmpOutputImage.deleteOnExit();
		doneFrames=0;
		/*
		ProcessTimeoutDestroyer readerDestroyer=new ProcessTimeoutDestroyer();
		readerDestroyer.setTimeout(1000*10);//for test;
		readerDestroyer.setProcess(fFMpegJpegPipeReader.getProcess());
		readerDestroyer.start();
		readerDestroyer.setDestroy(true);
		*/
		while((imgbyte=reader.nextImage())!=null){
			//readerDestroyer.setDestroy(false);
			/*if(doneFrames+1==28){
				log.infofinal frame");
				reader.close();
			}*/
			log.trace("readed:"+(doneFrames+1)+","+imgbyte.length+"bytes");
			try{
			//log.info("write frame:"+count+" byte="+imgbyte.length);
			//if(count>818){
		//		log.infostart:byte "+count+" imgsize="+imgbyte.length);
		//	}
			if(inputImageType==IMAGE_JPEG){
				FileUtils.writeByteArrayToFile(tmpInputImage, imgbyte);
			}else if(inputImageType==IMAGE_BMP){
				BufferedImage bimage=ImageIO.read(new ByteArrayInputStream(imgbyte));
				log.trace("read image:"+(doneFrames+1));
				if(bimage==null){
					log.warn("img:null");
					break;
				}
				ImageIO.write(bimage,"BMP",tmpInputImage);
				
			}
			//if(count>818){
		//		log.infobefore:effect "+count);
		//	}
			log.trace("pre effect:"+(doneFrames+1));
			effect(tmpInputImage,tmpOutputImage);//
			log.trace("effected:"+(doneFrames+1));
		//	if(count>818){
		//		log.infoafter:effect "+count);
		//	}
			if(outputImageType==IMAGE_JPEG){
				writer.writeJpeg(new FileInputStream(tmpOutputImage));
			}else if(outputImageType==IMAGE_BMP){
				BufferedImage bimage=ImageIO.read(tmpOutputImage);
				writer.writeJpeg(bimage);
			}
			
		//	if(count>818){
		//		log.infoafter:write "+count);
		//	}
			log.trace("writed:"+(doneFrames+1));
			doneFrames++;
			
			if(doneFrames%100==0){
				System.gc();
			}
			
			}catch(IOException e){
				e.printStackTrace();
				break;
				//show error message.
				//break;
			}
			//readerDestroyer.setDestroy(true);
			//readerDestroyer.setLasttime(System.currentTimeMillis());
		}
		//log.infoframes "+count);
		//readerDestroyer.setMonitering(false);//good bye.
		reader.close();
		writer.close();
	}
	public int getInputImageType() {
		return inputImageType;
	}
	public void setInputImageType(int inputImageType) {
		this.inputImageType = inputImageType;
	}
	public int getOutputImageType() {
		return outputImageType;
	}
	public void setOutputImageType(int outputImageType) {
		this.outputImageType = outputImageType;
	}
	public boolean isDoneRead() {
		return doneRead;
	}
	public void setDoneRead(boolean doneRead) {
		this.doneRead = doneRead;
	}
	public boolean isDoneWrite() {
		return doneWrite;
	}
	public void setDoneWrite(boolean doneWrite) {
		this.doneWrite = doneWrite;
	}
	public FFMpegJpegPipeReader getFFMpegJpegPipeReader() {
		return fFMpegJpegPipeReader;
	}
	public void setFFMpegJpegPipeReader(FFMpegJpegPipeReader mpegJpegPipeReader) {
		fFMpegJpegPipeReader = mpegJpegPipeReader;
	}
	public FFMpegJpegPipeWriter getFFMpegJpegPipeWriter() {
		return fFMpegJpegPipeWriter;
	}
	public void setFFMpegJpegPipeWriter(FFMpegJpegPipeWriter mpegJpegPipeWriter) {
		fFMpegJpegPipeWriter = mpegJpegPipeWriter;
	}

	
}
